#ifndef SANDBOX_SDK_EVENT_QUEUE
#define SANDBOX_SDK_EVENT_QUEUE

#include <stdint.h>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/member.hpp>

namespace sandbox
{
	namespace sdk
	{
		template <class Object, 
				  class Compare=std::equal<Object>,
				  class Hash=std::hash<Object>,
				  class Allocator=std::allocator<Object>
		> class collisionQueue
		{
		public:
			struct collisionEventTag {
				collisionEventTag():pevent(std::nullptr_t){}
				void * pevent;
				
				bool operator =(const collisionEventTag& other)
				{return pevent==std::nullptr_t ? false : (pevent==other.pevent);}
			};
			struct collisionTimeTag {
				collisionTimeTag(uint64_t t):time(t){}
				uint64_t time;
			};
			class eventSet 
			{
			public:
				eventSet(size_t reserve)
				{
				}
				void add(Object a, Object b, uint64_t attime)
				{
				}
				void sort();
			private:
				
			};
			
			enum class {CollisionWillHappen, CollisionWillNotHappen, SoftStop};			
			
			collisionQueue(uint64_t time):m_now(time){}
			
			uint64_t iteration(uint64_t max_time_from_now, Callback cb)
			{
				//return 'infinity' it there are no more events
				if(m_event_seq.empty())return UINT64_MAX;
				//get max time
				uint64_t maxtime=m_now+max_time_from_now;
				//get first event and see, if it happens earlier than maxtime
				//if not, return time when this event will happen
				auto I=m_event_seq.begin();
				
				RESTRICT(event *) ev=*I;
				if(ev->m_time > maxtime)return ev->m_time;
				//ok, event is going to happen in right time
				//remove it from the db
				m_event_seq.remove(I);
				//allocate space for result set
				eventSet rset(expected_result_set_size);
				//call callback on each object in event
				//note: these calls may cause rebinding to new events 
				//and we cannot invalidate these objects' tags, or 
				//callback will not know, whenever 2 objects belong to 
				//the same event. In that case, it may use optimizations
				//to avoid comparing A to B and then B to A
				//also, event can be deleted by unbind_object()
				//that's why we send address of current event. it must be
				//forwarded to any methods of collisionQueue the callback
				//will call to avoid unwanted unbindings
				cb(ev->m_time, ev->m_objects[0], rset, (void*)ev);
				cb(ev->m_time, ev->m_objects[1], rset, (void*)ev);
				//for rset, 
				//save event time and erase event - we've done with it
				uint64_t result=ev->m_time;
				m_event_allocator.erase(ev);
				return result;
			}			
		private:
			struct event {
				uint64_t m_time;
				Object   m_objects[2];
				
				struct compare_by_time {
					bool operator ()(const event* lhs, const event* rhs)const
					{
						if(lhs->m_time==rhs->m_time)return lhs<rhs;
						return lhs->m_time<rhs->m_time;
					}
				};
			};
			typedef std::multiset<event*, event::compare_by_time, Allocator> event_sequence_t;
			//we can use objects->events map, but it's faster
			//to require object to just store pointers to events
			//member variables==========================================
			SlabAllocator    m_event_allocator;
			event_sequence_t m_event_seq;
			uint64_t         m_now;
			//utility functions=========================================
			void collisionQueue::unbind_object(Object obj);
			

			template <class Callback, size_t expected_result_set_size=128>

			void iteration(Object * __restict__ objs, size_t count)
			{
				for(size_t iter=0; iter<count; ++iter)
				{
				}
			}
			template <class Callback> bool next_event(uint64_t time_from_now, Callback callback)
			{
				if(m_event_seq.empty())return false;
				
				event* ev=*m_event_seq.begin();
				if(ev->m_time > m_now+time_from_now)return false;
				
				callback(ev->m_object_count, ev->m_objects);
				
				return true;
			}
			void 
			
			
		public:
			event_queue():m_time(0),m_event_count(0){}
			
			void add_object(Object obj)
			{
				event_record rec;
				rec.id=m_event_count++;
				rec.time=m_time;
				rec.obj=obj;
				m_event_base.insert(rec);
			}
			void remove_object(Object obj)
			{
				auto& index=m_event_base.get<index_obj>();
				auto  iter =index.find(obj);
				if(iter!=index.end())index.erase(iter);
			}
			//method to calculate next event
			template <class Callback>
			bool closest_event(uint64_t max_from_now,
							   Callback callback)//must accept uint64_t,Object*
			{
				auto& timeIndex=m_event_base.get<index_time>();
				auto  range=timeIndex.
			}		   
			//method to post new event
			
			void post_event(uint64_t timefromnow, Object * objs, size_t count)
			{
				//we must decrement number of objects in events where object resides
			}
			//useful to retrieve information about objects in nearest event
			
			template <class T>
			bool closest_event(uint64_t limit_time, )
			{
				
			}
		};
		
		template <class Object, 
				  class Compare=std::equal<Object>,
				  class Hash=std::hash<Object>,
				  class Allocator=std::allocator<Object>
		> void collisionQueue::unbind_object(Object obj)
		{
			collisionEventTag tag=objs[iter].getTag<collisionEventTag>();			
			if(tag.pevent!=std::nullptr_t)
			{
				event * other   =(event*)tag.pevent;
				//make sure there were at least 2 colliding objects
				size_t  newcount=--other->m_object_count;
				assert(newcount>=1);
				//reposition object-ok to overwrite self
				other->m_objects[tag.position]=other->m_objects[newcount]
				//if event is to be deleted, unbind another object from event
				if(newcount<2){
					other->m_objects[0].setTag<collisionEventTag>(collisionEventTag());
					m_event_seq.erase(other);
					m_event_allocator.erase(other);
				//otherwise, just adjust moved object's tag
				}else{
					other->m_object_count=newcount;
					other->m_objects[tag.position].setTag(tag);
				}
			}			
		}
	}
}
#endif

<uid, time_t, object*>
//collision detection stages:
//
//1)check
//
//

////////t1//////
Ax-------|
Bx
Cx-------|
Dx-------|
ev1={A,C,D,t1}

///t2///t1//////
Ax-------|
Bx--|
Cx--|
Dx-------|
ev2={B,C,t2}
ev1={A,D,t1}
//iteration done====
/////t3/////////
A--------~
B---x
C---x--|
D------|
ev3={C,D,t3}
ev2={B,C,t2}
ev1=remove, only A left
//or=================
/////perform complicated calculations only if
/////unsure that new collision time with A is <= 
/////last collision time for A
A--------|--?
B---x-------?
C---x
D--------|

A--------|
B---x
C---x
D--------|
//iteration done====
/////////////t4////
//no valid collision events were detected at previous iteration
//but something changed on new iteration and we add a new collision
A--------x---|
B---*--------|
C---*
D--------x
//how to detect if object must be stopped? for example it is laying on the ground
//all collision tests must return either 'stop' or moving away
//we need an array which will contain all objects which hadn't are still 
/////t3/////////
A-------|--?
B--|-------?
C--|
D-------|
ev3={C,D,t3}
ev2={B,C,t2}
ev1=remove, only A left


A
B
C
Dx    x
